///////////////////////////////////////////////////////////////////////////////

#ifndef _SERVERBATTLEFIELD_H_
#include "serverbattlefield.h"
#endif

///////////////////////////////////////////////////////////////////////////////

#ifndef _SERVERENGINE_H_
#include "serverengine.h"
#endif

#ifndef _MAPONSERVER_H_
#include "maponserver.h"
#endif

#ifndef _TIMER_H_
#include "timer.h"
#endif

#include <iostream>

///////////////////////////////////////////////////////////////////////////////


ServerBattlefield::ServerBattlefield( ServerEngine* _serverEngine )
: m_Width(0),
  m_Height(0),
  m_ServerEngine(_serverEngine)
{
}


///////////////////////////////////////////////////////////////////////////////


ServerBattlefield::~ServerBattlefield()
{
}


///////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::Update( size_t _dt )
{
    DeleteBullets();
    ResetPlayers();
    ProcessUserInput(_dt);
    UpdatePhysics(_dt);
}


///////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::ProcessUserInput( size_t _dt )
{
    // Process shooting
    for (SPlayersByIDs::iterator it = m_SPlayersByIDs.begin(); it != m_SPlayersByIDs.end(); ++it )
    {
        SPlayer& player = it->second;
        player.ProcessInput(_dt);
        if ((m_Bullets.size()<MAX_BULLET_COUNT) && player.CanShoot(_dt) )
            m_Bullets.push_back( player.Shoot() );
    }
}


///////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::UpdatePhysics( size_t _dt )
{
    m_PhysicsEngine.Update(_dt);
}


///////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::AddPlayer( int _ID )
{
    // Add new player
    SPlayer newPlayer(_ID, ChoosePosition(), SWeapon(500) );
    m_SPlayersByIDs.insert( std::make_pair(_ID, newPlayer) );
}


///////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::SetClientInput( int _clientID,
                                        const ClientInput& _input,
                                        size_t _creationTime )
{
    GetMappedValue(m_SPlayersByIDs, _clientID).SetClientInput(_input, _creationTime);
}


////////////////////////////////////////////////////////////////////////////////


ServerState ServerBattlefield::GetState() const
{
    PlayersList players;
    for (SPlayersByIDs::const_iterator it = m_SPlayersByIDs.begin(); it != m_SPlayersByIDs.end(); ++it )
    {
        const SPlayer& sPlayer = it->second;
        Player player( sPlayer.GetID() );
        Point position( sPlayer.GetPosition().x * 10,
                        sPlayer.GetPosition().y * 10 );
        player.SetPosition(position);
        player.SetHealthPoints( sPlayer.GetHealthPoints() );
        players.push_back(player);
    }

    BulletsList bullets;
    for (SBulletsList::const_iterator it = m_Bullets.begin(); it != m_Bullets.end(); ++it)
    {
        const SBullet& sBullet = *it;
        Bullet bullet( sBullet.GetID() );
        Point position( sBullet.GetPosition().x * 10,
                        sBullet.GetPosition().y * 10 );
        bullet.SetPosition(position);
        bullets.push_back(bullet);
    }

    return ServerState( players, bullets, WallsList() );
}


////////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::RemovePlayer( int _ID )
{
    // Get player by ID
    SPlayer& player = GetMappedValue(m_SPlayersByIDs, _ID);

    // Remove it from engine
    m_PhysicsEngine.DestroyObject(player);
    m_SPlayersByIDs.erase(_ID);
}


////////////////////////////////////////////////////////////////////////////////


bool ServerBattlefield::HasPlayer( int _playerID ) const
{
    return ContainsKey(m_SPlayersByIDs, _playerID);
}


////////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::SetMap( MapOnServer& _map )
{
    m_PhysicsEngine.SetMap( _map );

    m_Width = _map.m_Width;
    m_Height = _map.m_Height;
    CreateWalls(_map.m_Circles);
    CreateWalls(_map.m_Polygons);
    CreateWalls(_map.m_Rects);
    m_RevivalPoints = _map.m_RevivalPoints;

    // Set default point
    if ( m_RevivalPoints.empty() )
    {
        Point pt(10.0f, 10.0f);
        m_RevivalPoints.push_back(pt);
    }
}


////////////////////////////////////////////////////////////////////////////////


template< typename _TColl >
void ServerBattlefield::CreateWalls( _TColl& _coll )
{
    for ( size_t i = 0; i < _coll.size(); i++ )
    {
        SWall wall( m_Walls.size(), _coll[i].second, _coll[i].first );
        m_Walls.push_back(wall);
    }
}


////////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::DeleteBullets()
{
    for ( SBulletsList::iterator it = m_Bullets.begin(); it != m_Bullets.end(); )
    {
        Point pt = it->GetPosition();
        // Is bullet outside of the field
        if ( it->IsDestroyed() || pt.x < 0 || pt.x >= m_Width || pt.y < 0 || pt.y >= m_Height )
        {
            // Delete bullet
            m_PhysicsEngine.DestroyObject(*it);
            it = m_Bullets.erase(it);
            continue;
        }
        ++it;
    }
}


////////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::ResetPlayers()
{
    for ( SPlayersByIDs::iterator it = m_SPlayersByIDs.begin(); it != m_SPlayersByIDs.end(); ++it )
    {
        if ( it->second.GetHealthPoints() <= 0 )
            // Revive
            it->second.Reset(ChoosePosition());
    }
}


////////////////////////////////////////////////////////////////////////////////


const Point& ServerBattlefield::ChoosePosition() const
{
    // Generate revival point for player
    srand ( Timer::GetCurrTime() );
    int ptNum = rand() % (int)m_RevivalPoints.size();

    // Revive
    return m_RevivalPoints[ptNum];
}


////////////////////////////////////////////////////////////////////////////////


void ServerBattlefield::CollideBulletWithPlayer( SBullet& _bullet, SPlayer& _player )
{
    // Do not react on destroyed bullet or dead player
    if ( _bullet.IsDestroyed() || _player.GetHealthPoints() <= 0 )
        return;

    _player.AddHealth( -_bullet.GetDamage() );
    if ( _player.GetHealthPoints() <= 0 )
    {
        std::cout << "Player #" << _player.GetID() << " was killed by bullet #"
            << _bullet.GetID() << std::endl;

        m_ServerEngine->HandleKilling( _player.GetID(), _bullet.GetPlayerID() );
    }
    else
    {
        std::cout << "Player #" << _player.GetID() << " was injured by bullet #"
            << _bullet.GetID() << std::endl;
    }
}


////////////////////////////////////////////////////////////////////////////////


PhysicsEngine& ServerBattlefield::Physics()
{
    return m_PhysicsEngine;
}


////////////////////////////////////////////////////////////////////////////////
